﻿using System;
using ArithmeticLibrary;

namespace Nolineq.Methods
{
    /// <summary>
    /// Resic rovnic metodou regula falsi.
    /// </summary>
    public class SecantMethod : EquationSolver
    {
        #region Fields

        private Iterators.Iterator iterator;

        #endregion // Fields

        #region Constructors

        /// <summary>
        /// Vytvori instanci resice rovnic metodou secen.
        /// </summary>
        /// <param name="function"></param>
        public SecantMethod(Function function, double start, double end, double root, double precision, int maxSteps)
            : base(function, start, end, root, precision, maxSteps)
        {
            this.Name = "Metoda regula falsi";
            this.iterator = new Iterators.SecantIterator(this);
        }

        #endregion // Constructors

        /// <summary>
        /// Vyresi rovnici metodou secen.
        /// </summary>
        /// <param name="start">Zacatek intravalu.</param>
        /// <param name="end">Konec intervalu.</param>
        /// <param name="precision">Presnost.</param>
        /// <returns></returns>
        public override Solution Solve()
        {

            // PREVENCE proti zamrznuti programu. Pocita pocet kroku pri vypoctu.
            int steps = 0;

            // Oznacime prvni nejvetsi interval.
            double left = Start; 
            double right = End;
            
            // jestli je nahodou koren v krajnim bode intervalu
            if (Function.Evaluate(left) == 0)
                return new Solution(left, 0);

            if (Function.Evaluate(right) == 0)
                return new Solution(right, 0);
            
            // vypocet prvniho pruseciku.
            Approximation = left - Function.Evaluate(left) * (right - left) / (Function.Evaluate(right) - Function.Evaluate(left));
            
            // Pokud funkcni hodnota kraju intervalu jsou na obe kladne nebo obe zaporne,
            // pak na tomto intervalu nemuzeme zajistit prusecik s osou x -> ukonceni vypoctu.
            if (Function.Evaluate(right) * Function.Evaluate(left) >= 0)
                // nevhodne zvolena metoda
                throw new UnsuitableMethodException("Nesplněna podmínka f(a) * f(b) < 0. Upravte vstupní interval [a, b] nebo zvolte jinou metodu.");
            else
            {
                // Dokud je delka intervalu a absolutni hodnota funkcni hodnoty pruseciku vetsi nez presnost. 
                while (Math.Abs(Function.Evaluate(Approximation)) >= Precision && steps < MaxSteps)
                {
                    steps++;

                    this.iterator.Iterate();
                }

                // kontrola prekroceni limitu iteraci
                if (steps == MaxSteps)
                    throw new UnsuitableMethodException("Překročen limit počtu iterací. Zkuste nastavit větší počet iterací, upravit počáteční parametry nebo zvolte jinou metodu.");
                
                return new Solution(Approximation, steps);
            }
        }

    }
}
